home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
ohlutil.zip
/
MV_DIR.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-31
|
6KB
|
208 lines
/* mv_dir -- rename directory
Copyright (C) 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Helper program for GNU mv on machines that lack the rename system call.
Usage: mv_dir from to
FROM must be an existing directory. TO must not exist.
Must be setuid root.
Ian Dall (ian@sibyl.eleceng.ua.oz.au)
and David MacKenzie (djm@ai.mit.edu) */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include "system.h"
#ifdef STDC_HEADERS
#include <stdlib.h>
#include <errno.h>
#else
char *malloc ();
extern int errno;
#endif
#ifndef HIPRI
#define HIPRI -10
#endif
#ifdef DEBUG
#define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
#define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
#endif
/* The name this program was run with. */
char *program_name;
char *basename ();
char *xmalloc ();
void error ();
void strip_trailing_slashes ();
/* Return the name of the directory containing PATH. */
char *
parent_dir (path)
char *path;
{
char *dir;
char *base;
int length;
base = rindex (path, '/');
if (base == NULL)
return ".";
if (base > path)
base--;
length = base - path + 1;
dir = xmalloc (length + 1);
strncpy (dir, path, length);
dir[length] = '\0';
return dir;
}
void
main (argc, argv)
int argc;
char **argv;
{
char *from, *to, *from_base, *from_parent, *to_parent;
struct stat from_stats, to_stats;
char *next_slash, temp;
int i;
program_name = argv[0];
if (argc != 3)
{
fprintf (stderr, "Usage: %s existing-dir new-dir\n", program_name);
exit (2);
}
from = argv[1];
to = argv[2];
strip_trailing_slashes (from);
strip_trailing_slashes (to);
from_parent = parent_dir (from);
to_parent = parent_dir (to);
/* Make sure `from' is not "." or "..". */
from_base = basename (from);
if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
error (1, 0, "cannot rename `.' or `..'");
/* Even with an effective uid of root, link fails if the target exists.
That is what we want, so don't unlink `to' first.
However, we do need to check that the directories that link and unlink
will modify are writable by the user. */
if (stat (from, &from_stats))
error (1, errno, "%s", from);
if ((from_stats.st_mode & S_IFMT) != S_IFDIR)
error (1, 0, "`%s' is not a directory", from);
if (access (from_parent, W_OK))
error (1, errno, "cannot write to `%s'", from_parent);
if (access (to_parent, W_OK))
error (1, errno, "cannot write to `%s'", to_parent);
/* We can't make this atomic, but we do our best. */
for (i = NSIG; i > 0; i--)
if (i != SIGKILL)
signal (i, SIG_IGN);
setuid (0); /* Make real uid 0 so it is harder to kill. */
nice (HIPRI - nice (0)); /* Raise priority. */
/* Make sure that `from' is not an ancestor of `to', to prevent
corruption of the file system in cases like
mv_dir foo foo/bar/baz
where foo and foo/bar are directories and foo/bar/baz does not exist. */
next_slash = to;
while ((next_slash = index (next_slash, '/')) != NULL)
{
temp = *++next_slash;
*next_slash = '\0';
if (stat (to, &to_stats))
error (1, errno, "%s", to);
*next_slash = temp;
if (to_stats.st_dev == from_stats.st_dev
&& to_stats.st_ino == from_stats.st_ino)
error (1, 0, "`%s' is an ancestor of `%s'", from, to);
}
if (link (from, to))
error (1, errno, "cannot link `%s' to `%s'", from, to);
if (unlink (from))
error (1, errno, "cannot unlink `%s'", from);
/* Replace the directory's `..' entry. It used to be a link to
the parent of `from'; make it a link to the parent of `to' instead. */
i = strlen (to);
next_slash = xmalloc (i + 4);
strcpy (next_slash, to);
strcpy (next_slash + i, "/..");
if (unlink (next_slash) && errno != ENOENT)
error (1, errno, "cannot unlink `%s'", next_slash);
if (link (to_parent, next_slash))
error (1, errno, "cannot link `%s' to `%s'", to_parent, next_slash);
exit (0);
}
/* Return NAME with any leading path stripped off. */
char *
basename (name)
char *name;
{
char *base;
base = rindex (name, '/');
return base ? base + 1 : name;
}
/* Allocate `n' bytes of memory dynamically, with error checking. */
char *
xmalloc (n)
unsigned n;
{
char *p;
p = malloc (n);
if (p == 0)
error (1, 0, "virtual memory exhausted");
return p;
}
/* Remove trailing slashes from STR;
they cause some system calls to fail. */
void
strip_trailing_slashes (str)
char *str;
{
int last = strlen (str) - 1;
while (last > 0 && str[last] == '/')
str[last--] = '\0';
}